#coding:utf-8

'''
event tree

we have BaseEvent as the root parent

and DelayEvent, CmdEvent as it's child
'''
import time
from Queue import Queue, Empty


class EvtFinished(Exception):
    pass


class BaseEvent(object):

    def isReady(self):
        raise NotImplementedError("child class {}".format(self.__class__))

    def isRepeat(self):
        return False

    def callBack(self):
        raise NotImplementedError("child class {}".format(self.__class__))


class IdleEvent(BaseEvent):

    def isReady(self):
        return True

    def isRepeat(self):
        return False

    def callBack(self):
        time.sleep(0.01)


class EmptyEvent(BaseEvent):

    def isReady(self):
        return True

    def isRepeat(self):
        return False

    def callBack(self):
        pass
    

class DelayEvent(BaseEvent):

    '''
    DelayEvent will be initialized and fired when created.
    
    then if it's repeat event, 
    return True from isReady on every interval is passwd

    '''

    def __init__(self,
                 callback = lambda : None,
                 args = (),
                 interval = 300,
                 repeat = True, # single_shot = False
                 ** kd_args):
        super(DelayEvent, self).__init__()
        self._start_time = time.time()
        self._repeat = repeat
        self._wait = interval
        self._callback = callback
        self._args = args
        self._firstrun = True

    def isReady(self):
        if self._firstrun:
            self._firstrun = False
            return True
        if not self._repeat:
            raise EvtFinished()
        curr = time.time()
        if self._start_time + self._wait < curr:
            return True
        return False

    def isRepeat(self):
        return self._repeat

    def __cmp__(self, evt):
        if self._start_time < evt._start_time:
            return -1
        elif self._start_time > evt._start_time:
            return 1
        else:
            return 0

    def callBack(self):
        # once the event has been processed, 
        # the interval shall be added
        self._start_time += self._wait
        self._callback(*self._args)


class QueueEvent(BaseEvent):

    def __init__(self,
                 queue,
                 quelength = 1,
                 callback = lambda : None,
                 timeout = 30,
                 args = (),
                 ** kd_args):
        '''
        @param timeout: if given None, this event won't be 
        ready unless all expected elements are filled.
        '''
        super(DelayEvent, self).__init__()

        self._start_time = time.time()
        # temp place to store the queue items
        self._elements = []
        self._wait_queue = queue
        self._length = 0
        self._quelength = quelength
        self._timeout = timeout
        self._callback = callback
        self._args = args

    def isReady(self):
        '''
        '''
        if self._length >= self._quelength:
            return True

        curr = time.time()
        if self._timeout is not None:
            if self._start_time + self._timeout < curr:
                return True

		if self._wait_queue.qsize() >= self._quelength:
			return True
		return False

    def isRepeat(self):
        return False

    def putElement(self, e):
        self._elements.append(e)

    def callBack(self):
        q = Queue()
        for e in self._elements:
            q.put(e)

        self._callback(q, *self.args)
